package usercenterservice

import (
	"context"
	"fmt"
	"gitee.com/go-mid/auth/authservice"
	"gitee.com/go-mid/idgen/idgenservice"
	"gitee.com/go-mid/infra/xcore"
	"gitee.com/go-mid/infra/xlog"
	"gitee.com/go-mid/infra/xsql/xdb"
	"gitee.com/go-mid/infra/xtime"
	"gitee.com/go-mid/user/internal/component"
	"gitee.com/go-mid/user/internal/dao"
	"gitee.com/go-mid/user/internal/entity"
	"gitee.com/go-mid/user/internal/util"
	"gitee.com/go-mid/user/userservice"
	"github.com/jinzhu/copier"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"strconv"
	"time"
)

type RegisterReq struct {
	userservice.RegisterParam

	IsLogin bool `json:"is_login"` // 是否注册后立即登录
} //@name 注册入参

type RegisterRes struct {
	LoginResult
	//是否新用户
	IsNew bool `json:"is_new"`
} //@name 注册响应

func (m *UsercenterServiceImpl) Register(ctx context.Context, req *RegisterReq) (res *RegisterRes, err error) {
	fun := "UsercenterServiceImpl.Register -->"
	st := xtime.NewTimeStat()
	defer func() {
		xlog.Infow(ctx, fun, "durationMs", st.Millisecond(), "req", req, "res", res, "err", err)
	}()
	userId, err := m.getUserIdByLoginid(ctx, req.LoginID)
	if err != nil {
		xlog.Warnf(ctx, "%s get loginid info error req: %v err: %s", fun, req, err)
		err = status.Error(codes.Internal, err.Error())
		return
	}
	//是否新用户
	isNewUser := userId <= 0
	//新用户执行注册
	if isNewUser {
		userId, err = m.doRegister(ctx, req)
		if err != nil {
			xlog.Warnf(ctx, "%s register error userId: %d req: %v err: %s", fun, userId, req, err)
			err = status.Error(codes.Internal, err.Error())
			return
		}
		//注册后置处理器
		m.postProcessRegister(ctx, userId, req)
	}
	var authInfo *AuthInfo
	if req.IsLogin {
		//执行登录
		authInfo, err = m.login(ctx, &LoginParam{
			LoginID: LoginID{
				Loginid:     req.Loginid,
				LoginidType: req.LoginidType,
			},
			UserId:     userId,
			ClientInfo: req.ClientInfo,
			AuthType:   req.AuthType,
		})
	}
	//把刚注册的用户信息读取出来，响应出去
	var userInfo *UserInfo
	if userRes, err := m.GetUser(ctx, &GetUserReq{
		UserID: userId,
	}); err != nil {
		xlog.Warnf(ctx, "%s get user error: req: %v, err: %v", fun, req, err)
	} else {
		userInfo = userRes.UserInfo
	}

	res = &RegisterRes{
		LoginResult: LoginResult{
			AuthInfo: authInfo,
			LoginID: LoginID{
				Loginid:     req.Loginid,
				LoginidType: req.LoginidType,
			},
			UserInfo: userInfo,
		},
		IsNew: isNewUser,
	}
	return
}

type BindLoingidReq struct {
	LoginID
	UserId int64
}

type BindLoingidRes struct {
	//之前绑定过，
	HasBind bool `json:"has_bind"`
	//之前绑定过，但是被解绑了，本次操作进行了重新绑定
	IsRebind bool   `json:"is_rebind"`
	Loginid  string `json:"loginid"`
}

func (m *UsercenterServiceImpl) BindLoingid(ctx context.Context, req *BindLoingidReq) (res *BindLoingidRes, err error) {
	fun := "UsercenterServiceImpl.BindLoingid -->"
	st := xtime.NewTimeStat()
	defer func() {
		xlog.Infow(ctx, fun, "durationMs", st.Millisecond(), "req", req, "res", res, "err", err)
	}()
	if req.UserId <= 0 {
		xlog.Warnf(ctx, "%s invalid param req: %v  ", fun, req)
		err = status.Error(codes.InvalidArgument, "userid invalid")
		return
	}
	oldLoginid, err := m.getLoginid(ctx, req.LoginID)
	if err != nil {
		xlog.Warnf(ctx, "%s get loginid info error req: %v err: %s", fun, req, err)
		err = status.Error(codes.Internal, err.Error())
		return
	}
	if oldLoginid != nil {
		if oldLoginid.State == int(EnumLoginidState_NORMAL) {
			if oldLoginid.UserID != req.UserId {
				xlog.Warnf(ctx, "%s loginid has bind to another userid error req: %v binduserid: %d ", fun, req, oldLoginid.UserID)
				err = status.Error(codes.Internal, fmt.Sprintf("loginid has bind to another userid: %d", oldLoginid.UserID))
				return
			}
			res = &BindLoingidRes{
				HasBind: true,
				Loginid: oldLoginid.Loginid,
			}
			return
		}
		// 之前存在记录，则把状态修改为正常即可
		_, err = component.XDBUser.Update(ctx, entity.UserLoginidTable, map[string]interface{}{
			"id": oldLoginid.ID,
		}, map[string]interface{}{
			"state": EnumLoginidState_NORMAL,
		})
		if err != nil {
			xlog.Warnf(ctx, "%s Update UserLoginidTable err req: %v err: %s", fun, req, err)
			return
		}
		res = &BindLoingidRes{
			HasBind:  true,
			IsRebind: true,
			Loginid:  req.Loginid,
		}
		return
	}

	now := time.Now().Unix()
	var registerLoginId = &entity.UserLoginid{
		Ct:          now,
		Ut:          now,
		UserID:      req.UserId,
		LoginidType: int(req.LoginidType),
		Loginid:     m.encryLoginid(ctx, req.LoginID),
		State:       int(EnumLoginidState_NORMAL),
	}
	loginidUps, err := manager.BuildDbSqlMap(ctx, registerLoginId, true)
	if err != nil {
		xlog.Warnf(ctx, "%s build loginid ups error: req: %v, err: %v", fun, req, err)
		err = status.Error(codes.Internal, err.Error())
		return
	}
	// 保存账号和登录凭证  绑定关系
	_, err = component.XDBUser.Insert(ctx, entity.UserLoginidTable, []map[string]interface{}{loginidUps})
	if err != nil {
		xlog.Warnf(ctx, "%s insert loginid err req: %v err: %s", fun, req, err)
		err = status.Error(codes.Internal, err.Error())
		return
	}

	res = &BindLoingidRes{
		Loginid: req.Loginid,
	}
	return
}

type LoginReq struct {
	LoginParam
	//登录校验码 可以是短信验证码、密码等跟当前登录类型关联的校验码
	LoginCode
} //@name 登录入参
type LoginResult struct {
	//登录认证信息
	AuthInfo *AuthInfo `json:"auth_info"`
	LoginID
	//用户信息
	UserInfo *UserInfo `json:"user_info"`
} //@name 登录结果

type LoginRes struct {
	LoginResult
} //@name 登录响应

func (m *UsercenterServiceImpl) Login(ctx context.Context, req *LoginReq) (res *LoginRes, err error) {
	fun := "UsercenterServiceImpl.Login -->"
	st := xtime.NewTimeStat()
	defer func() {
		xlog.Infow(ctx, fun, "durationMs", st.Millisecond(), "req", req, "res", res, "err", err)
	}()
	userId, err := m.getUserIdByLoginid(ctx, req.LoginID)
	if err != nil {
		xlog.Warnf(ctx, "%s get loginid info error req: %v err: %s", fun, req, err)
		err = status.Error(codes.Internal, err.Error())
		return
	}
	if userId <= 0 {
		xlog.Warnf(ctx, "%s not found loginid info error req: %v  ", fun, req)
		err = status.Error(codes.NotFound, "账号或密码错误")
		return
	}
	var userInfo *UserInfo
	userRes, err := m.GetUser(ctx, &GetUserReq{
		UserID: userId,
	})
	if err != nil {
		xlog.Warnf(ctx, "%s get user error: req: %v, err: %v", fun, req, err)
		err = status.Error(codes.NotFound, "账号或密码错误")
		return
	} else {
		userInfo = userRes.UserInfo
	}
	if userInfo == nil || userInfo.UserID <= 0 {
		xlog.Warnf(ctx, "%s not found user: req: %v ", fun, req)
		err = status.Error(codes.NotFound, "账号或密码错误")
		return
	}
	//校验验证码
	switch req.LoginCodeType {
	//账号密码
	case EnumLoginCodeType_Passwd:
		if encryPasswd(req.LoginCode.LoginCode) != userInfo.Passwd {
			xlog.Warnf(ctx, "%s not match  passwd: req: %v ", fun, req)
			err = status.Error(codes.NotFound, "账号或密码错误")
			return
		}
	}

	//执行登录
	authInfo, err := m.login(ctx, &LoginParam{
		LoginID: LoginID{
			Loginid:     req.Loginid,
			LoginidType: req.LoginidType,
		},
		UserId:     userId,
		ClientInfo: req.ClientInfo,
		AuthType:   req.AuthType,
	})

	res = &LoginRes{
		LoginResult: LoginResult{
			AuthInfo: authInfo,
			LoginID: LoginID{
				Loginid:     req.Loginid,
				LoginidType: req.LoginidType,
			},
			UserInfo: userInfo,
		},
	}
	return
}
